import java.io.*;
import java.util.concurrent.locks.Lock;

public class SharedObject implements Serializable, SharedObject_itf {
	
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	
	public int id;  				 //référence unique de l'objet sur le serveur
	public SharedObjectState lock;	 //verrou locale de l'objet
	public Object obj;				 //référence sur la valeur locale de l'objet partagé
									
	
    // invoked by the user program on the client node
    public synchronized void lock_read() {
    	System.out.println("SharedObject:" + this + " lock_read avec verrou initial : " + lock);
    	
    	switch(lock){
    		
    		case NL:
    			obj = Client.lock_read(id);
        		while(obj == null){
        			try {
    						wait(20);
    				} catch (InterruptedException e) {
    						// TODO Auto-generated catch block
    						e.printStackTrace();
    				}
    				obj = Client.lock_read(id);
    				System.out.println("Boucle nulle lock_read");
        		}
    			lock = SharedObjectState.RLT;
    			break;
    		
    		case RLC:
    			lock = SharedObjectState.RLT;
    			break;
    		
    		case WLC:
    			lock = SharedObjectState.RLT_WLC;
    			break;
    			
    		default:
			try {
				throw new InvalidLockException(id, lock, "READ LOCK");
			} catch (InvalidLockException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
    	}
    	System.out.println("SharedObject:" + this + " lock_read avec verrou : " + lock);
    }

    // invoked by the user program on the client node
    public synchronized void lock_write() {
    	System.out.println("SharedObject:" + this + " lock_write avec verrou initial : " + lock);
    	switch(lock){
    		
    	case NL:
    	case RLC:
    		obj = Client.lock_write(id);
    		while(obj == null){
    			try {
						wait(20);
				} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
				}
				obj = Client.lock_write(id);
				System.out.println("Boucle nulle du lock_write");
    		}
    		
    		if(obj == null)
    		{
    			System.out.println("Impossible de finir le lock_write en concurrence");
    		}
    		
    		lock = SharedObjectState.WLT;
    		break;
    		
    	case WLC:
    		lock = SharedObjectState.WLT;
    		break;
    	
    	default:
    	try{
    		throw new InvalidLockException(id, lock, "WRITE LOCK");
    	} catch (InvalidLockException e){
    		e.printStackTrace();
    	}
    	}
    	System.out.println("SharedObject:" + this + " loc_write avec verrou : " + lock);
    }

    // invoked by the user program on the client node
    public synchronized void unlock() {
    	System.out.println("SharedObject:" + this + " unlock avec verrou initial : " + lock);
    	switch(lock){
    		
    		case RLT: 
    			lock = SharedObjectState.RLC;
    			notify();
    			break;
    		
    		case WLT:
    			lock = SharedObjectState.WLC;
    			notify();
    			break;
    			
    		case RLT_WLC:
    			lock = SharedObjectState.WLC;
    			notify();
    			break;
    			
    		case NL: 
			try {
				throw new InvalidLockException(id, lock, "NL");
			} catch (InvalidLockException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
    	}
    	System.out.println("SharedObject:" + this + " unlock avec verrou : " + lock);
    }

    
    // callback invoked remotely by the server
    public synchronized Object reduce_lock() {
    	System.out.println("SharedObject:" + this + "reduce_lock avec verrou initial : " + lock);
    	switch(lock){
    	
    		case WLT:
				try {
					wait();
					lock = SharedObjectState.RLC;
					break;
					
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			
    		case WLC:
    			lock = SharedObjectState.RLC;
    			break;
    		
    		case RLT_WLC:
    			lock = SharedObjectState.RLT;
    			break;
    		
			default:
			try {
				throw new InvalidLockException(id, lock, "REDUCE_LOCK");
			} catch (InvalidLockException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
    	}
    	
    	System.out.println("SharedObject:" + this + "reduce_lock avec verrou : " + lock);
    	return obj;
    }

    // callback invoked remotely by the server
    public synchronized void invalidate_reader() {
    	System.out.println("SharedObject:" + this + "invalidate_reader avec verrou initial : " + lock);
    	switch(lock){
    		
    		case RLT:
    			try {
					wait();
					lock = SharedObjectState.NL;
	    			break;
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
    			
    		case RLC:
    			lock = SharedObjectState.NL;
    			break;
    			
    		default:
			try {
				throw new InvalidLockException(id, lock, "INVALIDATE_READER");
			} catch (InvalidLockException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
    	}
    	System.out.println("SharedObject:" + this + "invalidate_reader avec verrou : " + lock);
    }

    public synchronized Object invalidate_writer() {
    	System.out.println("SharedObject:" + this + " invalidate_writer avec verrou initial: " + lock);
    	switch(lock){
    		
    		case RLT_WLC:
			try {
				wait();
				lock = SharedObjectState.NL;
    			break;
			} catch (InterruptedException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
    		
    		case WLT:
			try {
				wait();
    			lock = SharedObjectState.NL;
    			break;
			} catch (InterruptedException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}

    		
    		case WLC:
    			lock = SharedObjectState.NL;
    			break;
    			
    		default:
    			try{
    				throw new InvalidLockException(id, lock, "INVALIDATE_WRITER");
    			} catch(InvalidLockException e){
    				
    				e.printStackTrace();
    			}
    	}
    	
    	System.out.println("SharedObject:" + this + " invalidate_writer avec verrou : " + lock);
    	return obj;
    }
    
    
    
    /**
	 * @return the id
	 */
	public int getId() {
		return id;
	}

	/**
	 * @param id the id to set
	 */
	public void setId(Integer id) {
		this.id = id;
	}

	/**
	 * @return the lock
	 */
	public SharedObjectState getLock() {
		return lock;
	}

	/**
	 * @param lock the lock to set
	 */
	public void setLock(SharedObjectState lock) {
		this.lock = lock;
	}

	/**
	 * @return the obj
	 */
	public Object getObj() {
		return obj;
	}

	/**
	 * @param obj the obj to set
	 */
	public void setObj(Object obj) {
		this.obj = obj;
	}
	


	class InvalidLockException extends Exception{
    	
    	/**
		 * 
		 */
		private static final long serialVersionUID = 1L;

		public InvalidLockException(Integer id, SharedObjectState lock, String action){
    		
    		System.out.println("Objet partagé : " + id);
    		System.out.println("Vous demandez un verrou [" + action + " ] dans un état qui ne vous le permet pas : [ " + lock + " ]");
    	}
    }
}
